package main

import (
	"crypto/tls"
	"encoding/hex"
	"io/ioutil"
	"os/exec"
	"os/user"
	"regexp"
	"strconv"
	"strings"

	//"errors"
	"fmt"
	"net/http"
	"time"
	//"io/fs"
)

var (
	// Highest valid capability of the running kernel.
	CAP_LAST_CAP            = Cap(40)
	GlobalVulnerabilityMap  = make(map[string]string)
	GlobalClient            *http.Client
	GlobalIsAppArmorRunning bool
	GlobalSeccompRunning    bool
	GlobalToken string

)
type Cap int
// Defined in https://github.com/torvalds/linux/blob/master/include/uapi/linux/capability.h
const (
	CAP_CHOWN = Cap(0)
	CAP_DAC_OVERRIDE = Cap(1)
	CAP_DAC_READ_SEARCH = Cap(2)
	CAP_FOWNER = Cap(3)
	CAP_FSETID = Cap(4)
	CAP_KILL = Cap(5)
	CAP_SETGID = Cap(6)
	CAP_SETUID = Cap(7)
	CAP_SETPCAP = Cap(8)
	CAP_LINUX_IMMUTABLE = Cap(9)
	CAP_NET_BIND_SERVICE = Cap(10)
	CAP_NET_BROADCAST = Cap(11)
	CAP_NET_ADMIN = Cap(12)
	CAP_NET_RAW = Cap(13)
	CAP_IPC_LOCK = Cap(14)
	CAP_IPC_OWNER = Cap(15)
	CAP_SYS_MODULE = Cap(16)
	CAP_SYS_RAWIO = Cap(17)
	CAP_SYS_CHROOT = Cap(18)
	CAP_SYS_PTRACE = Cap(19)
	CAP_SYS_PACCT = Cap(20)
	CAP_SYS_ADMIN = Cap(21)
	CAP_SYS_BOOT = Cap(22)
	CAP_SYS_NICE = Cap(23)
	CAP_SYS_RESOURCE = Cap(24)
	CAP_SYS_TIME = Cap(25)
	CAP_SYS_TTY_CONFIG = Cap(26)
	CAP_MKNOD = Cap(27)
	CAP_LEASE = Cap(28)
	CAP_AUDIT_WRITE   = Cap(29)
	CAP_AUDIT_CONTROL = Cap(30)
	CAP_SETFCAP       = Cap(31)
	CAP_MAC_OVERRIDE = Cap(32)
	CAP_MAC_ADMIN = Cap(33)
	CAP_SYSLOG = Cap(34)
	CAP_WAKE_ALARM = Cap(35)
	CAP_BLOCK_SUSPEND = Cap(36)
	CAP_AUDIT_READ = Cap(37)
	CAP_PERFMON = Cap(38)
	CAP_BPF = Cap(39)
	CAP_CHECKPOINT_RESTORE = Cap(40)

	DockerSock = "Docker sock breakout"
	Kubelet = "Kubelet"
	MountBreakOut = "Mount breakout"
	VarLogEscape = "Var Log Escape"
	CVE20195736 = "CVE-2019-5736"
	//DeepceDockerExploit = "Deepce docker exploit"
	CGroupEscape  = "Cgroup breakout"
	CapSysExploit = "Kernel module breakout"
	PathSecrets   = "/var/run/secrets/kubernetes.io/serviceaccount/token"
)

// HELPER FUNCTIONS

func (c Cap) String() string {
	switch c {
	case CAP_CHOWN:
		return "chown"
	case CAP_DAC_OVERRIDE:
		return "dac_override"
	case CAP_DAC_READ_SEARCH:
		return "dac_read_search"
	case CAP_FOWNER:
		return "fowner"
	case CAP_FSETID:
		return "fsetid"
	case CAP_KILL:
		return "kill"
	case CAP_SETGID:
		return "setgid"
	case CAP_SETUID:
		return "setuid"
	case CAP_SETPCAP:
		return "setpcap"
	case CAP_LINUX_IMMUTABLE:
		return "linux_immutable"
	case CAP_NET_BIND_SERVICE:
		return "net_bind_service"
	case CAP_NET_BROADCAST:
		return "net_broadcast"
	case CAP_NET_ADMIN:
		return "net_admin"
	case CAP_NET_RAW:
		return "net_raw"
	case CAP_IPC_LOCK:
		return "ipc_lock"
	case CAP_IPC_OWNER:
		return "ipc_owner"
	case CAP_SYS_MODULE:
		return "sys_module"
	case CAP_SYS_RAWIO:
		return "sys_rawio"
	case CAP_SYS_CHROOT:
		return "sys_chroot"
	case CAP_SYS_PTRACE:
		return "sys_ptrace"
	case CAP_SYS_PACCT:
		return "sys_pacct"
	case CAP_SYS_ADMIN:
		return "sys_admin"
	case CAP_SYS_BOOT:
		return "sys_boot"
	case CAP_SYS_NICE:
		return "sys_nice"
	case CAP_SYS_RESOURCE:
		return "sys_resource"
	case CAP_SYS_TIME:
		return "sys_time"
	case CAP_SYS_TTY_CONFIG:
		return "sys_tty_config"
	case CAP_MKNOD:
		return "mknod"
	case CAP_LEASE:
		return "lease"
	case CAP_AUDIT_WRITE:
		return "audit_write"
	case CAP_AUDIT_CONTROL:
		return "audit_control"
	case CAP_SETFCAP:
		return "setfcap"
	case CAP_MAC_OVERRIDE:
		return "mac_override"
	case CAP_MAC_ADMIN:
		return "mac_admin"
	case CAP_SYSLOG:
		return "syslog"
	case CAP_WAKE_ALARM:
		return "wake_alarm"
	case CAP_BLOCK_SUSPEND:
		return "block_suspend"
	case CAP_AUDIT_READ:
		return "audit_read"
	case CAP_PERFMON:
		return "perfmon"
	case CAP_BPF:
		return "bpf"
	case CAP_CHECKPOINT_RESTORE:
		return "checkpoint_restore"
	}
	return "unknown"
}

func TestMappingUserNS()error{
	cmd := `ls -l /proc/$$/ns | grep 4026531837 || cat /proc/self/uid_map | grep  "0[[:space:]]*0"`
	//2 options which leads to exit 0:
	//Same user NS - checked with Nimrod about this situation
	//Not in the same user NS and the mapping is  0		0
	//In this 2 cases root is mapped to root
	_, err := exec.Command("sh", "-c", cmd).Output()
	if err != nil {
		fmt.Printf(red+"[!]"+defaultColor+" The container user name space is not with the host user name space and UID 0 is mapped to UID different from 0  : %s\n", err.Error())
		return err
	}

	return nil
}

func testExecuteCommand(command string) error{
	cmd := `
	if ! [ -x "$(command -v `+command+`)" ]; then
	exit 1
	fi`
	_, err := exec.Command("sh", "-c", cmd).Output()
	if err != nil {
		fmt.Printf(red+"[!]"+defaultColor+" Could not test the vulnerability, the %s doesn't exists or you cant run it\n",command)
		return err
	}
	return nil
}

func getRootFSDevice()(string, error){
	var err error
	var device string
	foundUUID := false
	dat, err := ioutil.ReadFile("/proc/cmdline")
	if err == nil {
		cmdline := string(dat)
		splittedCmdLine := strings.Split(cmdline, " ")

		var uuid string

		// extracting the UUID of the device which hold the root file system
		for _, splitLine := range splittedCmdLine {
			if strings.HasPrefix(splitLine, "root=UUID") || strings.HasPrefix(splitLine, "root=PARTUUID")  {
				//uuid = splitLine[10:]
				split2 := strings.Split(splitLine, "=")
                                uuid = split2[len(split2) - 1]
				foundUUID = true
				break
			}
		}

		if foundUUID {
			cmd := exec.Command("blkid")
			var stdout []byte
			stdout, err = cmd.Output()

			if err == nil {
				//fmt.Println(string(stdout))
				lines := strings.Split(string(stdout), "\n")
				for _, line := range lines {
					if strings.Contains(line, uuid) {
						deviceSplitted := strings.Split(line, ":")
						device = deviceSplitted[0]
					}
				}
			}

		}
	}
	if err != nil {
		fmt.Printf(red+"[!]"+defaultColor+" Could not test the vulnerability due to the error: %s",err)
	}
	return device, err
}

func decodeHexToCap (hexCap uint64) map[string]bool{
	var capInt uint64 = 1
	capSet := make(map[string]bool)
	for i := Cap(0); i <= CAP_LAST_CAP; i++ {
		if capInt & hexCap != 0 {
			capSet[i.String()] = true
		}

		capInt = capInt << 1
	}
	return capSet
}

func getCap()(map[string]bool,error){

	cmd := `cat /proc/$$/status | grep CapEff`
	out, err := exec.Command("sh", "-c", cmd).Output()
	if err != nil {
		fmt.Printf(red+"[!]"+defaultColor+" Could not test the vulnerability due to the error which occur while excute shell command to get the capEff, error: %s\n", err.Error())
		return nil,err
	}

	splitOut := strings.Split(string(out), "\t")
	hexCap, err := strconv.ParseUint(strings.TrimSuffix(splitOut[1], "\n"), 16, 64)
	if err != nil {
		fmt.Printf(red+"[!]"+defaultColor+" Could not test the vulnerability due to the error which occur while excute shell command to get the capEff, error: %s\n", err.Error())
		return nil,err
	}

	uint64Cap := hexCap
	capSet := decodeHexToCap(uint64Cap)
	return capSet,nil
}

func getDefaultGateway() (string, error) {
	cmd := `cat  /proc/net/route`
	out, err := exec.Command("sh", "-c", cmd).Output()

	pattern := `\n[^[:space:]]*?[[:space:]]00000000[[:space:]](.*?)[[:space:]]`
	r, err := regexp.Compile(pattern)
	if err != nil {
		fmt.Printf(red+"[!]"+defaultColor+" Could not test the vulnerability due to the error: %s\n", err.Error())
		return "", err
	}
	match := r.FindAllStringSubmatch(string(out), -1)

	gateway := match[0][1]
	a, _ := hex.DecodeString(gateway)
	return fmt.Sprintf("%v.%v.%v.%v", a[3], a[2], a[1], a[0]), nil
}

func testConnectionToKubelet(nodeIp string, httpHeaderKey string, httpHeaderValue string, kubeletApi string) (bool,error) {
	req, _ := http.NewRequest("GET", fmt.Sprintf("https://%s:10250%s", nodeIp, kubeletApi), nil)
	req.Header.Set(httpHeaderKey, httpHeaderValue)
	resp, err := GlobalClient.Do(req)
	if err != nil {
		fmt.Printf(red+"[!]"+defaultColor+" Http request to kubelet failed due to the error:%s.Ending test \n",err.Error())
		return false, err
	}
	statusOK := resp.StatusCode >= 200 && resp.StatusCode < 300
	if statusOK {
		return true,nil
	}else{
		fmt.Printf(red+"[!]"+defaultColor+" Could no establish connection with kubelet, http status code respond:%s \n",resp.StatusCode)
		return false, nil
	}
}
//
//func getMountPathIfExists() (string, error) {
//	cmd := `cat /proc/self/mountinfo | grep /var/log || true`
//	out, err := exec.Command("sh", "-c", cmd).Output()
//	//checkError(err)
//	if err != nil {
//		fmt.Printf(red+"[!]"+defaultColor+" Could not test the vulnerability due to the error which occur while excutinn shell command to test the mount path to /var/log, error: %s\n", err.Error())
//		return "",err
//	}
//	pattern := ".*? .*? .*? (.*?) (.*?) .*?\n"
//	r, err := regexp.Compile(pattern)
//	//checkError(err)
//	if err != nil {
//		fmt.Printf(red+"[!]"+defaultColor+" Could not test the vulnerability due to the error: %s \n", err.Error())
//		return "",err
//	}
//	match := r.FindAllStringSubmatch(string(out), -1)
//	for _, matchInner := range match {
//		if matchInner[1] == "/var/log" {
//			return matchInner[2],nil
//		}
//	}
//	return "",nil
//}

// https://kubernetes.io/docs/tutorials/clusters/apparmor/
// https://lwn.net/Articles/656307/
// http://terenceli.github.io/%E6%8A%80%E6%9C%AF/2019/02/04/seccomp
// https://dockerlabs.collabnix.com/advanced/security/apparmor/#no-profile
// https://gitlab.com/apparmor/apparmor/-/wikis/AppArmorInterfaces
func testSeccompAndAppArmor(){
	//Test which profile is loaded to the container
	cmd := `cat /proc/$$/status | grep -e "Seccomp:[[:space:]]*0"`
	_, err := exec.Command("sh", "-c", cmd).Output()
	//if there is error i take it as not found
	if err != nil {
		fmt.Printf(cyan+"[i]"+defaultColor+" The container is running with seccomp with SECCOMP_MODE_FILTER mode or SECCOMP_MODE_STRICT\n")
		GlobalSeccompRunning = true
	}

	// Test if the process have apparmor profile loaded
	cmd = `cat /proc/$$/attr/apparmor/current | grep unconfined`
	_, err = exec.Command("sh", "-c", cmd).Output()
	if err != nil {
		fmt.Printf(cyan+"[i]"+defaultColor+" The container is running with apparmor profile\n")
		GlobalIsAppArmorRunning = true
	}
}

func modifyVulnerabilityMap(exploitName string, newInfo string){
	if info, found := GlobalVulnerabilityMap[exploitName]; found{
		GlobalVulnerabilityMap[exploitName] = info + newInfo
		return
	}
	if newInfo != "" {
		GlobalVulnerabilityMap[exploitName] = fmt.Sprintf("*** %s, %s",exploitName,newInfo)
		return
	}

	GlobalVulnerabilityMap[exploitName] = fmt.Sprintf("%s",exploitName)
}

func modifyByAppArmorSeccomp(exploitName string, BlockedObject string){
	if BlockedObject != "" {
		BlockedObject = fmt.Sprintf("such as %s",BlockedObject)
	}

	if GlobalIsAppArmorRunning &&  GlobalSeccompRunning{
		modifyVulnerabilityMap(exploitName,fmt.Sprintf("AppArmor and Seccomp is running, High probability it will block relevant requirements for the exploit %s\n",BlockedObject))
		printInfo(exploitName)
		return
	}

	if GlobalIsAppArmorRunning {
		modifyVulnerabilityMap(exploitName,fmt.Sprintf("AppArmor is running, High probability it will block relevant requirements for the exploit %s\n",BlockedObject))
		printInfo(exploitName)
		return
	}

	if GlobalSeccompRunning {
		modifyVulnerabilityMap(exploitName,fmt.Sprintf("Seccomp is running, Might block relevant requirements for the exploit %s\n",BlockedObject))
		printInfo(exploitName)
		return
	}
	modifyVulnerabilityMap(exploitName,"")
	printInfo(exploitName)
}

func printInfo (exploitName string){
	if info, found := GlobalVulnerabilityMap[exploitName]; found{
		fmt.Printf(yellow+"[+]"+defaultColor+" The container is "+red+"vulnerable"+defaultColor+" to %s\n",info)
		return
	}
}

func isRoot() (bool,error) {
	currentUser, err := user.Current()
	if err != nil {
		fmt.Printf(red+"[!]"+defaultColor+" Could not test the vulnerability due to the error which occur while testing if the user is root, error: %s \n",err.Error())
		return false, err
	}
	return currentUser.Username == "root",nil
}

func InitHttpClient() {
	tr := &http.Transport{
		MaxIdleConns:       10,
		IdleConnTimeout:    30 * time.Second,
		DisableCompression: true,
		TLSClientConfig:    &tls.Config{InsecureSkipVerify: true},
	}

	GlobalClient = &http.Client{
		Transport: tr,
		Timeout:   time.Second * 20,
	}
}

// Check if the user name is part of the input group
func testUserGroup(group string) (bool, error){
	currentUser, err := user.Current()
	if err != nil {
		fmt.Printf(red+"[!]"+defaultColor+" Could not test the vulnerability due to the error which occur while testing if the user is root, error: %s. Ending test\n",err.Error())
		return false, err
	}

	cmd := `getent group ` + group + ` | grep '\b` + currentUser.Username + `\b' || true`
	out, err := exec.Command("sh", "-c", cmd).Output()
	if err != nil {
		fmt.Printf(red+"[!]"+defaultColor+" Could not test the vulnerability due to the error which occur while excutinn shell command to test if the current user is part of the docker group, error: %s. Ending test\n", err.Error())
		return false,err
	}

	return string(out) != "",nil
}

func setKubernetesToken () error {
	data, err := ioutil.ReadFile(PathSecrets)
	if err!=nil {
		fmt.Printf(red+"[!]"+defaultColor+" Could not open the requested token under %s", PathSecrets)
		return err
	}

	token := string(data)
	bearerTemp := "Bearer " + token
	GlobalToken = bearerTemp
	return nil
}

func checkMountPath (path string, mountOption string) (string,error){
	var cmd string
	if mountOption != "" {
		cmd = `if  cat /proc/self/mountinfo | grep -q '^[^[:space:]]*[[:space:]][^[:space:]]*[[:space:]][^[:space:]]*[[:space:]]`+path+`[^[:space:]]*[[:space:]][^[:space:]]*[[:space:]]`+mountOption+`[[:space:],]'; then
	   path=$(cat /proc/self/mountinfo | grep '^[^[:space:]]*[[:space:]][^[:space:]]*[[:space:]][^[:space:]]*[[:space:]]`+path+`[^[:space:]]*[[:space:]][^[:space:]]*[[:space:]]`+mountOption+`[[:space:],]' | cut -d' ' -f 5)
       echo $path
	 else
	   exit 1
	 fi`
	} else{
		cmd = `if  cat /proc/self/mountinfo | grep -q '^[^[:space:]]*[[:space:]][^[:space:]]*[[:space:]][^[:space:]]*[[:space:]]`+path+`'; then
	   path=$(cat /proc/self/mountinfo | grep '^[^[:space:]]*[[:space:]][^[:space:]]*[[:space:]][^[:space:]]*[[:space:]]`+path+`' | cut -d' ' -f 5)
       echo $path
	 else
	   exit 1
	 fi`
	}

	out, err := exec.Command("sh", "-c", cmd).Output()
	if err != nil {
		var message string
		if mountOption == "" {
			message = fmt.Sprintf(red+"[!]"+defaultColor+" The requested path:%s is not mounted. Ending test\n",path)
		}else{
			message = fmt.Sprintf(red+"[!]"+defaultColor+" The requested path:%s is not mounted with the requested mount option:%s. Ending test\n",path,mountOption)
		}
		fmt.Printf("%s",message )
		return "",err
	}
	return strings.TrimSuffix(string(out), "\n"),nil
}
//END HELPER FUNCTIONS
//START TESTS

func dockerSockTest(){
	fmt.Printf(cyan+"[i]"+defaultColor+" Start of %s vulnerability test \n",DockerSock)
	_,err:= checkMountPath("docker.sock","rw")
	if err != nil {
		return
	}

	errUserNS := TestMappingUserNS()
	isRootCheck, errRoot := isRoot()
	isPartOfDockerGroup, errGroupTest := testUserGroup("docker")
	if errRoot == nil && errGroupTest == nil && (isRootCheck && errUserNS == nil || isPartOfDockerGroup) {
		fmt.Printf(yellow+"[+]"+defaultColor+" The container is "+red+"vulnerable"+defaultColor+" to  %s \n",DockerSock)
		modifyVulnerabilityMap(DockerSock,"")
		return
	}

	if errRoot != nil || errGroupTest != nil {
		return
	}
}

func kubeletTest(nodeIp string) {
	fmt.Printf(cyan+"[i]"+defaultColor+" Start of %s vulnerability test \n",Kubelet)
	succeedConnectionToKubelet, err := testConnectionToKubelet(nodeIp,"Content-Type","application/x-www-form-urlencod", "/healthz")
	if succeedConnectionToKubelet && err == nil {
		fmt.Printf(yellow+"[+]"+defaultColor+" The container is "+red+"vulnerable"+defaultColor+" to %s\n",Kubelet)
		modifyVulnerabilityMap(Kubelet,"")
	}
}

//https://linoxide.com/understanding-each-entry-of-linux-fstab-etcfstab-file/
//https://community.hpe.com/t5/General/mount-only-root-can-do-that-why/td-p/4467747#.YS5F6o4zYZB
func mountBreakoutTest() {
	fmt.Printf(cyan+"[i]"+defaultColor+" Start of %s vulnerability test \n",MountBreakOut)
	capMap,err := getCap()
	if err != nil {
		return
	}

	if _, ok := capMap[CAP_SYS_ADMIN.String()]; !ok {
		fmt.Printf(red+"[!]"+defaultColor+" the container is not "+red+"vulnerable"+defaultColor+" to %s exploit, user doesn't have sys_admin cap \n",MountBreakOut)
		return
	}

	device, errRootFS := getRootFSDevice()
	if errRootFS != nil {
		return
	}
	if device == "" {
		fmt.Printf(red+"[!]"+defaultColor+" The container is not "+red+"vulnerable"+defaultColor+" to %s exploit, doesn't expose to the root FS device \n",MountBreakOut)
		return
	}

	isRootCheck, errRoot := isRoot()
	if errRoot != nil {
		return
	}

	if  isRootCheck {
		errUserNS := TestMappingUserNS()
		if errUserNS != nil {
			fmt.Printf(red+"[!]"+defaultColor+" The container is not root in the host user NS, therefore it depends if the device has user mount option in the host /etc/fstab\n")
			modifyVulnerabilityMap(MountBreakOut,"depends if the device has user mount option in the host /etc/fstab\n")
			modifyByAppArmorSeccomp(MountBreakOut, "Mount")
			return
		}

	}else{
		fmt.Printf(red+"[!]"+defaultColor+" The container is not root, therefore it depends if the device has user mount option in the host /etc/fstab\n")
		modifyVulnerabilityMap(MountBreakOut,"depends if the device has user mount option in the host /etc/fstab\n")
		modifyByAppArmorSeccomp(MountBreakOut, "Mount")
		return
	}

	modifyByAppArmorSeccomp(MountBreakOut, "Mount")

}

//TODO Think about adding the anonymous call to here and also to var log escape as well
func varLogTest(nodeIp string){
	fmt.Printf(cyan+"[i]"+defaultColor+" Start of var log escape vulnerability test \n")
	var err error
	var succeedConnectionToKubelet bool
	var defaultGateway string
	err = setKubernetesToken()
	if err != nil {
		return
	}

	if nodeIp != "" {
		succeedConnectionToKubelet, err = testConnectionToKubelet(nodeIp,"Authorization", GlobalToken, "/logs/")
	}else {
		defaultGateway, err = getDefaultGateway()
		if err != nil {
			return
		}
		succeedConnectionToKubelet, err = testConnectionToKubelet(defaultGateway,"Authorization", GlobalToken, "/logs/")
	}

	if !succeedConnectionToKubelet || err != nil {
		return
	}

	errUserNS := TestMappingUserNS()
	path, errMount := checkMountPath("/var/log","")
	isRootCheck, errRoot := isRoot()
	if  errUserNS == nil && errMount == nil && errRoot == nil && path != "" && isRootCheck {
		fmt.Printf(yellow+"[+]"+defaultColor+" The container is "+red+"vulnerable"+defaultColor+" to var log escape")
		modifyVulnerabilityMap(VarLogEscape,"")
	}else{
		if errRoot != nil {
			return
		}
	}
}

//TODO - think about add this cap also
// CAP_DAC_OVERRIDE - Bypass file read, write, and execute permission checks.
//              (DAC is an abbreviation of "discretionary access
//              control".)
func CVE20195736Test(){
	fmt.Printf(cyan+"[i]"+defaultColor+" Start of CVE-2019-5736 vulnerability test \n")
	errUserNS := TestMappingUserNS()
	isRootCheck, errRoot := isRoot()
	if  errRoot == nil && isRootCheck == false {
		fmt.Printf(red+"[!]"+defaultColor+" Not root user on the host and requires root privilege on the host to overwrite the runC binary \n")
		return
	}

	if errUserNS != nil {
		fmt.Printf(red+"[!]"+defaultColor+" The container is not root in the host user NS and requires root privilege on the host to overwrite the runC binary \n")
		return
	}

	if errRoot != nil {
		return
	}

	fmt.Printf(yellow+"[+]"+defaultColor+" The container is "+red+"vulnerable"+defaultColor+" to %s \n",CVE20195736)
	modifyVulnerabilityMap(CVE20195736,"Depends if your Docker version is below 18.09.2 and runC version is below 1.0-rc6")
}

func cGroupsEscape() {
	fmt.Printf(cyan+"[i]"+defaultColor+" Start of %s vulnerability tests \n", CGroupEscape)
	isRootCheck, errRoot := isRoot()
	if errRoot == nil && isRootCheck {
		capMap, err := getCap()
		if err != nil {
			return
		}
		if _, ok := capMap[CAP_SYS_ADMIN.String()]; !ok {
			fmt.Printf(cyan+"[i]"+defaultColor+" No CAP_SYS_ADMIN, Check if the cgroup RDMA is read-write \n")
			cmd := `cat /proc/self/mountinfo | grep -q '^[^[:space:]]*[[:space:]][^[:space:]]*[[:space:]][^[:space:]]*[[:space:]][^[:space:]]*[[:space:]]/sys/fs/cgroup/rdma[[:space:]]rw[[:space:],]'`
			_, err := exec.Command("sh", "-c", cmd).Output()
			if err != nil {
				fmt.Printf(red+"[!]"+defaultColor+" Cgroup RDMA is not mounted read-write. Ending test\n")
			} else {
				fmt.Printf(yellow+"[+]"+defaultColor+" The container is "+red+"vulnerable"+defaultColor+" to %s \n", CGroupEscape)
				modifyVulnerabilityMap(CGroupEscape,"")
			}
			return
		}

		cmd := `cat /proc/cgroups | grep -q "rdma"`
		_, err = exec.Command("sh","-c", cmd).Output()
		if err != nil {
			fmt.Printf (red+"[!]"+defaultColor+" Cgroup RDMA is not compiled into the kernel. Ending test \n")
			return
		}

		// CAP_SYS_ADMIN exists therefore apparmor might block the mount sys call
		modifyByAppArmorSeccomp(CGroupEscape, "Mount")
	}
}

func capSysModule(){
	fmt.Printf(cyan+"[i]"+defaultColor+" Start of %s vulnerability tests \n", CapSysExploit)
	errMake := testExecuteCommand("make")
	errInsmod := testExecuteCommand("insmod")
	if errMake != nil || errInsmod != nil {
		return
	}

	cmd := `
	RED=$(tput bold)$(tput setaf 1)
	DEFAULT_COLOR=$(tput sgr0)
	if ! [ -d "/lib/modules/$(uname -r)" ]; then
    echo "${RED}[i] ${DEFAULT_COLOR} The directory /lib/modules/$(uname -r) is required to run this exploit."
    exit 1
    fi
	
    if ! [ -d "$(readlink -f  /lib/modules/$(uname -r)/build)" ]; then
    echo "${RED}[i] ${DEFAULT_COLOR}$(readlink -f  /lib/modules/$(uname -r)/build) are required to run this exploit."
    exit 1
    fi

    if ! [ -d "$(dirname $(readlink -f $(readlink -f  /lib/modules/$(uname -r)/build)/Makefile))" ]; then
    echo "${RED}[i] ${DEFAULT_COLOR}$(dirname $(readlink -f $(readlink -f  /lib/modules/$(uname -r)/build)/Makefile)) are required to run this exploit."
    exit 1
    fi`
	out, err := exec.Command("sh", "-c", cmd).Output()
	
	if err != nil {
		fmt.Println(string(out))
		fmt.Printf(red+"[!]"+defaultColor+" Ending test\n")
		return
	}

	capMap,err := getCap()
	if err != nil {
		return
	}
	if _, ok := capMap[CAP_SYS_MODULE.String()]; ok {
		modifyVulnerabilityMap(CapSysExploit,"")
		fmt.Printf(yellow+"[+]"+defaultColor+" The container is "+red+"vulnerable"+defaultColor+" to %s \n", CapSysExploit)
	}
}

const red string = "\u001B[1;31m"
const green string= "\u001B[1;32m"
const yellow string= "\u001B[1;33m"
const blue string = "\u001B[1;34m"
const cyan string = "\u001B[1;36m"
const defaultColor string = "\u001B[0m"

func mainfunc(nodeIp string){
	fmt.Printf("\n"+ blue +"========= "+green+"LOGS"+blue+" ========="+defaultColor+"\n")
	testSeccompAndAppArmor()
	InitHttpClient()
	dockerSockTest()
	kubeletTest(nodeIp)
	mountBreakoutTest()
	varLogTest(nodeIp)
	CVE20195736Test()
	cGroupsEscape()
	capSysModule()

	fmt.Printf("\n"+ blue +"========= "+green+"RESULTS"+blue+" ========="+defaultColor+"\n")
	if len(GlobalVulnerabilityMap) != 0 {
		//fmt.Printf(yellow+"[+]"+green+" The container is "+red+"vulnerable"+defaultColor+" to the exploits below:\n")
		fmt.Printf(green+"The container is vulnerable to the following exploits:\n")
		for _, info := range GlobalVulnerabilityMap {
			fmt.Printf(yellow+"[+]"+green+" %s\n",info)
		}
		return
	}

	fmt.Printf(cyan+"[i]"+defaultColor+" The container is not "+red+"vulnerable"+defaultColor+" to non of kubesploit exploits")
}
//
//func main (){
//	//
//	//InitHttpClient()
//	//testSeccompAndAppArmor()
//	//mainfunc("127.0.0.1")
//
//}
